// CBSplineAnimator by ikam
// based on the Rob Bateman opengl exemple of b-spline
//
// from the forums: http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=22670
//

#include "ISceneNodeAnimator.h"

class CBSplineAnimator : public ISceneNodeAnimator
{
public:
   CBSplineAnimator(u32 time, core::array<core::vector3df> points, f32 speed = 1.0f);
   virtual ~CBSplineAnimator ();
   virtual void animateNode (scene::ISceneNode *node, u32 timeMs);

protected:
   int clamp(s32 idx, u32 size);

   core::array<core::vector3df> mPoints;
   u32 mStartTime;
   f32 mSpeed;
};


CBSplineAnimator::CBSplineAnimator(u32 time, core::array<core::vector3df> points, f32 speed)
{
   mStartTime = time;
   mPoints = points;
   mSpeed = speed;
}

CBSplineAnimator::~CBSplineAnimator ()
{
}


inline int CBSplineAnimator::clamp(s32 idx, u32 size)
{
   return ( idx<0 ? 0 : ( idx>=size ? size-1 : idx ) );
}


void CBSplineAnimator::animateNode (scene::ISceneNode *node, u32 timeMs)
{
   u32 pSize = mPoints.size();    

   const f32 dt = ( (timeMs-mStartTime) * mSpeed * 0.001f );
   const f32 u = core::fract ( dt );
   s32 idx = core::floor32( dt ) % (pSize+1);
   f32 iu = 1.0f-u;

   float b0 = iu * iu * iu / 6.0f;
   float b1 = ( 3 * u * u * u - 6 * u * u + 4 ) / 6.0f;
   float b2 = (-3 * u * u * u + 3 * u * u + 3* u + 1 ) /6.0f;
   float b3 = u * u * u /6.0f;     

   core::vector3df f, p0, p1, p2, p3;
   p0 = mPoints[ clamp( idx-2, pSize ) ];
   p1 = mPoints[ clamp( idx-1, pSize ) ];
   p2 = mPoints[ clamp( idx+0, pSize ) ];
   p3 = mPoints[ clamp( idx+1, pSize ) ];

   f = b0 * p0 + b1 * p1 + b2 * p2 + b3 * p3;       

   // set node orientation
   core::vector3df direction = node->getPosition() - f;
   node->setRotation(direction.getHorizontalAngle());

   // set node position
   node->setPosition(f);   
} 